package com.oig.sys.auth.service.impl;

import cn.hutool.core.util.StrUtil;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.BadRequestException;
import com.oig.common.module.SysRequest;
import com.oig.sys.auth.dao.ITbRoleDao;
import com.oig.sys.auth.dto.PermissionDto;
import com.oig.sys.auth.dto.RoleDto;
import com.oig.sys.auth.entity.TbRole;
import com.oig.sys.auth.service.ITbJobRoleService;
import com.oig.sys.auth.service.ITbResourcePermissionService;
import com.oig.sys.auth.service.ITbRolePermissionService;
import com.oig.sys.auth.service.ITbRoleResourceService;
import com.oig.sys.auth.service.ITbRoleService;
import com.oig.sys.auth.service.ITbRoleSystemService;
import com.oig.sys.auth.service.ITbUserRoleService;
import com.oig.sys.auth.vo.RoleVo;
import com.oig.sys.security.util.SecurityUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * @author wuxl
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class TbRoleServiceImpl implements ITbRoleService {

	private final ITbRoleDao tbRoleDao;
	private final ITbRoleSystemService roleSystemService;
	private final ITbRoleResourceService roleResourceService;
	private final ITbRolePermissionService rolePermissionService;
	private final ITbJobRoleService jobRoleService;
	private final ITbUserRoleService userRoleService;
	private final ITbResourcePermissionService resourcePermissionService;


	@Override
	public RoleVo getRoleInfo(Integer roleId) {
		RoleVo roleVo = tbRoleDao.getVoById(roleId);
		if (roleVo == null) {
			throw new BadRequestException("角色id错误");
		}
		roleVo.setRoleSystemList(this.roleSystemService.findSystemByRole(roleId));
		roleVo.setRolePermissionList(this.rolePermissionService.findPermissionByRole(roleId));
		roleVo.setRoleResourceList(this.roleResourceService.findResourceByRole(roleId));
		return roleVo;
	}

	@Transactional
	@Override
	public RoleVo addRole(RoleDto roleDto) {
		log.debug("role  dto:{}",roleDto);
		TbRole role = new TbRole();
		BeanUtils.copyProperties(roleDto, role,"id");
		this.checkOrgzRole(role);
		role.setCreateUserId(SecurityUtil.getDefineUserMain().getId());
		role.setCreateUserName(SecurityUtil.getDefineUserMain().getRealName());
		role.setCreateTime(new Date());
		if (StringUtils.isBlank(roleDto.getNoDelFlag())){
			role.setNoDelFlag(CommonEnum.FlagEnum.TRUE_FLAG.getCode());
		}
		this.tbRoleDao.save(role) ;
		RoleVo roleVo = new RoleVo() ;
		BeanUtils.copyProperties(role,roleVo);
		if (roleDto.getRoleSystemList()!=null && !roleDto.getRoleSystemList().isEmpty()){
			roleVo.setRoleSystemList(this.roleSystemService.addRoleSystem(role.getId(),roleDto.getRoleSystemList()));
		}
		if (roleDto.getRoleResourceList()!=null && !roleDto.getRoleResourceList().isEmpty()){
			roleVo.setRoleResourceList(this.roleResourceService.addRoleResource(role.getId(),roleDto.getRoleResourceList()));
			//查询菜单的接口
			List<Integer> permissionIdList = this.resourcePermissionService.findPermissionId(roleDto.getRoleResourceList()) ;
			roleDto.setRolePermissionList(this.mergePermission(permissionIdList, roleDto.getRolePermissionList()));
		}
		//resource里是有permission的，这里考虑不用前端传
		if (roleDto.getRolePermissionList()!=null && !roleDto.getRolePermissionList().isEmpty()){
			roleVo.setRolePermissionList(this.rolePermissionService.addRolePermission(role.getId(), roleDto.getRolePermissionList()));
		}
		return roleVo;
	}


	private List<PermissionDto> mergePermission(List<Integer> permissionIdList, List<PermissionDto> rolePermissionDtoList){
		if (permissionIdList==null||permissionIdList.isEmpty()){
			return rolePermissionDtoList;
		}
		List<PermissionDto> permissionDtoList = new ArrayList<>();
		if (rolePermissionDtoList!=null){
			for (PermissionDto permissionDto : rolePermissionDtoList){
				Iterator<Integer> iterator=permissionIdList.iterator();
				while (iterator.hasNext()){
					Integer permissionId = iterator.next();
					if (permissionDto.getId().equals(permissionId)){
						iterator.remove();
						break;
					}
				}
			}
			permissionDtoList.addAll(rolePermissionDtoList);
		}
		for (Integer permissionId : permissionIdList){
			PermissionDto permissionDto = new PermissionDto();
			permissionDto.setId(permissionId);
			permissionDtoList.add(permissionDto) ;
		}
		return permissionDtoList;
	}

	private void checkOrgzRole(TbRole role){
		if (role==null){
			throw new BadRequestException("id错误") ;
		}
		if (SecurityUtil.getDefineUserMain().getTenantId()!=0){
			if (!role.getTenantId().equals(SecurityUtil.getDefineUserMain().getTenantId())){
				throw new BadRequestException("无租户权限");
			}
		}
	}



	@Transactional
	@Override
	public RoleVo updateRole(RoleDto roleDto) {
		TbRole role = this.tbRoleDao.getTbRoleById(roleDto.getId()) ;
		log.debug("role dto:{}, entity:{}",roleDto, role);
		this.checkOrgzRole(role);
		BeanUtils.copyProperties(roleDto, role, "id");
		role.setLastUpdateId(SecurityUtil.getDefineUserMain().getId());
		role.setLastUpdateName(SecurityUtil.getDefineUserMain().getRealName());
		role.setLastUpdateTime(new Date());
		this.tbRoleDao.save(role) ;
		RoleVo roleVo = new RoleVo() ;
		BeanUtils.copyProperties(role,roleVo);
		if (roleDto.getRoleSystemList()!=null && !roleDto.getRoleSystemList().isEmpty()){
			roleVo.setRoleSystemList(this.roleSystemService.updateRoleSystem(role.getId(),roleDto.getRoleSystemList()));
		}
		if (roleDto.getRoleResourceList()!=null && !roleDto.getRoleResourceList().isEmpty()){
			roleVo.setRoleResourceList(this.roleResourceService.updateRoleResource(role.getId(),roleDto.getRoleResourceList()));
			//根据菜单构建一个 PermissionDtoList
			List<Integer> permissionIdList = this.resourcePermissionService.findPermissionId(roleDto.getRoleResourceList()) ;
			roleDto.setRolePermissionList(this.mergePermission(permissionIdList, roleDto.getRolePermissionList()));
		}
		//resource里是有permission的，这里考虑不用前端传
		if (roleDto.getRolePermissionList()!=null && !roleDto.getRolePermissionList().isEmpty()){
			roleVo.setRolePermissionList(this.rolePermissionService.updateRolePermission(role.getId(), roleDto.getRolePermissionList()));
		}
		return roleVo;
	}

	@Transactional
	@Override
	public void delRole(Integer roleId) {
		TbRole role = this.tbRoleDao.getTbRoleById(roleId) ;
		log.debug("roleId:{}, entity:{}",roleId, role);
		this.checkOrgzRole(role);
		if (CommonEnum.FlagEnum.FALSE_FLAG.getCode().equals(role.getNoDelFlag())){
			throw new BadRequestException("不允许删除");
		}
		this.roleSystemService.delRoleSystemByRoleId(roleId);
		this.roleResourceService.delRoleResourceByRoleId(roleId);
		this.rolePermissionService.delRolePermissionByRoleId(roleId);
		this.jobRoleService.delJobRoleByRoleId(roleId);
		this.userRoleService.delUserRoleByRoleId(roleId);
		this.tbRoleDao.delete(role);
	}

	@Transactional
	@Override
	public void addRoleSystem(Integer roleId, List<Integer> systemIdList) {
		TbRole role = this.tbRoleDao.getTbRoleById(roleId) ;
		log.debug("roleId:{}, entity:{}",roleId, role);
		this.checkOrgzRole(role);
		this.roleSystemService.addRoleSystemId(roleId, systemIdList);
	}

	@Transactional
	@Override
	public void delRoleSystem(Integer roleId, List<Integer> systemIdList) {
		TbRole role = this.tbRoleDao.getTbRoleById(roleId) ;
		log.debug("roleId:{}, entity:{}",roleId, role);
		this.checkOrgzRole(role);
		this.roleSystemService.delRoleSystemId(roleId, systemIdList);
	}

	@Transactional
	@Override
	public void addRoleResource(Integer roleId, List<Integer> resourceIdList) {
		TbRole role = this.tbRoleDao.getTbRoleById(roleId) ;
		log.debug("roleId:{}, entity:{}",roleId, role);
		this.checkOrgzRole(role);
		this.roleResourceService.addRoleResourceId(roleId,resourceIdList) ;
	}

	@Transactional
	@Override
	public void delRoleResource(Integer roleId, List<Integer> resourceIdList) {
		TbRole role = this.tbRoleDao.getTbRoleById(roleId) ;
		log.debug("roleId:{}, entity:{}",roleId, role);
		this.checkOrgzRole(role);
		this.roleResourceService.delRoleResourceId(roleId, resourceIdList);
	}

	@Transactional
	@Override
	public void addRolePermission(Integer roleId, List<Integer> permissionIdList) {
		TbRole role = this.tbRoleDao.getTbRoleById(roleId) ;
		log.debug("roleId:{}, entity:{}",roleId, role);
		this.checkOrgzRole(role);
		this.rolePermissionService.addRolePermissionId(roleId,permissionIdList);
	}

	@Transactional
	@Override
	public void delRolePermission(Integer roleId, List<Integer> permissionIdList) {
		TbRole role = this.tbRoleDao.getTbRoleById(roleId) ;
		log.debug("roleId:{}, entity:{}",roleId, role);
		this.checkOrgzRole(role);
		this.rolePermissionService.delRolePermissionId(roleId, permissionIdList);
	}


	@Override
	public List<RoleVo> listAllRole(SysRequest<RoleDto> roleRequest) {
		if (roleRequest.getParamDto()==null){
			throw new BadRequestException("至少传角色所属机构参数") ;
		}
		Specification<TbRole> specification = new Specification<TbRole>(){
			@Override
			@Nullable
			public Predicate toPredicate(@NonNull Root<TbRole> root, @NonNull CriteriaQuery<?> criteriaQuery, @NonNull CriteriaBuilder criteriaBuilder) {
				List<Predicate> predicateList = new ArrayList<>();
				Path<Integer> orgzIdPath = root.get("tenantId");
				predicateList.add(criteriaBuilder.equal(orgzIdPath, roleRequest.getParamDto().getTenantId()));
				if (StrUtil.isNotBlank(roleRequest.getParamDto().getRoleName())){
					Path<String> roleNamePath = root.get("roleName");
					predicateList.add(criteriaBuilder.like(roleNamePath, "%"+roleRequest.getParamDto().getRoleName()+"%"));
				}
				if (StrUtil.isNotBlank(roleRequest.getParamDto().getRoleCode())){
					Path<String> roleCodePath = root.get("roleCode");
					predicateList.add(criteriaBuilder.like(roleCodePath, "%"+roleRequest.getParamDto().getRoleCode()+"%"));
				}
				criteriaQuery.where(predicateList.toArray(new Predicate[0]));
				return criteriaQuery.getRestriction();
			}
		};
		List<TbRole> roleList  = this.tbRoleDao.findAll(specification);
		List<RoleVo> roleVoList = new ArrayList<>();
		for (TbRole role : roleList){
			RoleVo roleVo = new RoleVo();
			BeanUtils.copyProperties(role,roleVo);
			roleVoList.add(roleVo);
		}
		return roleVoList;
	}
}
